﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Security;

namespace ccgstore.WebApp.Models
{
    public class CcgStoreRepository
    {
        public CCGStoreEntities Entities = new CCGStoreEntities();

        private const string MissingUser = "User does not exist";
        private const string TooManyUser = "User already exists";

        public int NumberOfUsers
        {
            get
            {
                return Entities.Users.Count();
            }
        }

        public CcgStoreRepository()
        {
            Entities = new CCGStoreEntities();
        }

        public IQueryable<User> GetAllUsers()
        {
            return Entities.Users.OrderBy(u => u.Username);
        }

        public User GetUser(int id)
        {
            return Entities.Users.FirstOrDefault(u => u.UserId == id);
        }

        public User GetUser(string username)
        {
            return Entities.Users.FirstOrDefault(u => u.Username == username);
        }

        public IQueryable<User> GetUsersForRole(Role role)
        {
            return Entities.Users.Where(u => u.UserRoles.Any(ur => ur.Role == role));
        }

        public IEnumerable<Role> GetRolesForUser(string username)
        {
            return GetRolesForUser(GetUser(username));
        }

        public IEnumerable<Role> GetRolesForUser(User user)
        {
            if (!UserExists(user))
                throw new ArgumentException(MissingUser);
            return user.UserRoles.Select(ur => ur.Role);
        }

        public void AddRolesToUser(User user, IEnumerable<Role> roles)
        {
            foreach (var role in roles)
            {
                AddRoleToUser(user, role);
            }
        }

        private void AddRoleToUser(User user, Role role)
        {
            var userRole = Entities.UserRoles.Create();
            userRole.User = user;
            userRole.Role = role;

            Entities.SaveChanges();
        }

        public bool UserExists(User user)
        {
            if (user == null)
                return false;

            return (Entities.Users.SingleOrDefault(u => u.UserId == user.UserId || u.Username == user.Username) != null);
        }

        private void AddUser(User user)
        {
            if(UserExists(user)) throw new ArgumentException(TooManyUser);
            Entities.Users.Add(user);
        }

        public void CreateUser(string username, string name, string password, string email, IEnumerable<Role> roles)
        {

            if (string.IsNullOrEmpty(username.Trim()))
                throw new ArgumentException("Invalid username");
            if (string.IsNullOrEmpty(name.Trim()))
                throw new ArgumentException("Invalid name");
            if (string.IsNullOrEmpty(password.Trim()))
                throw new ArgumentException("Invalid password");
            if (string.IsNullOrEmpty(email.Trim()))
                throw new ArgumentException("Invalid email");
            if (Entities.Users.Any(u => u.Username == username))
                throw new ArgumentException("Username taken");

            var newUser = new User
            {
                Username = username,
                Name = name,
// ReSharper disable CSharpWarnings::CS0618
                Password = FormsAuthentication.HashPasswordForStoringInConfigFile(password, "md5"),
// ReSharper restore CSharpWarnings::CS0618
                Email = email             
            };

            try
            {
                AddUser(newUser);

                var enumerable = roles as IList<Role> ?? roles.ToList();

                if (enumerable.Any())
                    AddRolesToUser(newUser, enumerable);
            }
            catch (ArgumentException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new ArgumentException(String.Format("Authentication provider error: {0}", e));
            }

            Save();
        }

        public void DeleteUser(User user)
        {
            if (!UserExists(user))
                throw new ArgumentException(MissingUser);
            Entities.Users.Remove(user);
        }

        public void DeleteUser(string username)
        {
            DeleteUser(GetUser(username));
        }

        public void Save()
        {
            Entities.SaveChanges();
        }
    }
}